/**
 * Title:        Utility Class Library<p>
 * Description:  Some useful utility classes.<p>
 */
package com.timber.util;

import java.text.*;

/**
  *  This class handles the formatting for floating point numbers
  *
  *  Additional validation is provided:
  *  <li> Maximum Length
  *
  *  @author
  *  @date
  */
public class NumberFormatter extends Formatter
{
  protected int precision;           // how many digits to show after the decimial (-1: not fixed)
  protected int fieldSize;           // how many characters available for display (0: no limit
  protected boolean thousandCommas;  // Whether or not to display commas
  protected boolean showSign;        // Whether or not to prefix positive numbers with '+'
  protected boolean blankZero;       // Whether to suppress zero values (display as blank space)
  protected String prefix;           // Add a prefix to the value
  protected NumberFormat nf;
  protected char[] allowableChars;

  /**
   * Constructor
   * @param precision how many digits to show after the decimial (-1: not fixed)
   * @param fieldSize how many characters available for display (0: no limit
   * @param commas Whether or not to display commas
   * @param showSign Whether or not to prefix positive numbers with '+'
   * @param blankZero Whether to suppress zero values (display as blank space)
   */
  public NumberFormatter( int precision,
			  int fieldSize,
			  boolean commas,
			  boolean sign,
			  boolean zero,
			  String prefix,
			  char[] allowableChars )
  {
    this.precision = precision;
    this.fieldSize = fieldSize;
    this.thousandCommas = commas;
    this.showSign = sign;
    this.blankZero = zero;
    this.prefix = prefix;
    this.allowableChars = allowableChars;

    nf = NumberFormat.getInstance();
    nf.setMaximumFractionDigits( precision );
    nf.setMinimumFractionDigits( precision );
    nf.setGroupingUsed( thousandCommas );
  }

  public NumberFormatter( int precision )
  {
    this( precision, 0, true, false, 0 );
  }

  public NumberFormatter( int precision, int fieldSize, boolean commas, boolean sign )
  {
    this( precision, fieldSize, commas, sign, 0 );
  }

  public NumberFormatter( int precision, int fieldSize, boolean commas, boolean sign, int ticks )
  {
    this.precision = precision;
    this.fieldSize = fieldSize;
    this.thousandCommas = commas;
    //	  this.displaySign = sign;
    //	  this.ticks = ticks;

    nf = NumberFormat.getInstance();
    nf.setMaximumFractionDigits( precision );
    nf.setMinimumFractionDigits( precision );
    nf.setGroupingUsed( thousandCommas );
  }

  /**
   * Constructor
   * Creates a default NumberFormatter with no precision limit, no field size limit, commas,
   * no sign, no blankZeros, and no leading spaces.
   */
  public NumberFormatter ()
  {
    this( -1, 0, true, false, false, null, null );
  }

  /**
   * Formats an object to produce a string.
   */
  public String format( Object o )
  {
    if( o instanceof Double )
    {
      double d = ( ( Double )o ).doubleValue();
      return format( d );
    }
    else
    {
      double d;
      try
      {
	d = parseDouble( ( String )o );
      }
      catch( ParseException e )
      {
	System.out.println( "Parse Exception caught formatting: " + o + ". " + e.toString() );
	// Do nothing for now.
	return null;
      }
      return format( d );
    }
  }

  /**
   * Formats an object to produce a string.
   */
  public String format( Double o )
  {
    double d = ( ( Double )o ).doubleValue();
    return format( d );
  }

  public String format( double d )
  {
    if( prefix != null )
      return ( prefix + nf.format( d ) );
    else
      return nf.format( d );
  }

  public Object parseObject( String s ) throws ParseException
  {
    return parse( s );
  }


  public Double parse( String s ) throws ParseException
  {
    return new Double( parseDouble( s ) );
  }

  public double parseDouble( String s ) throws ParseException
  {
    StringBuffer buffer = new StringBuffer();
    double mult = 1.0;
    boolean gotPeriod = false;

    if (s == null)
       return 0.0;

    for( int i = 0; i < s.length(); i++ )
    {
      char c = s.charAt( i );

      switch( c )
      {
	case 'k':
	case 'K':
	  if( mult != 1.0 )
	    throw new ParseException( "Too many multipliers", i );
	  mult = 1000.0;
	  if( !gotPeriod )
	    buffer.append( '.' );
	  break;

	case 'm':
	case 'M':
	  if( mult != 1.0 )
	    throw new ParseException( "Too many multipliers", i );
	  mult = 1000000.0;
	  if( !gotPeriod )
	    buffer.append( '.' );
	  break;

	case ',':
	  break;

	case 'f':
	case 'd':
	  throw new ParseException( "Bad character", i );

	case '.':
	  buffer.append( c );
	  gotPeriod = true;
	  break;

	default:
	  buffer.append( c );
	  break;
      }
    }

    try
    {
      String temp = buffer.toString().trim();
      if( temp.equals( "." ) || temp.equals( "" ) )
	temp = "0.0";

      Double d = new Double( temp );
      return d.doubleValue() * mult;
    }
    catch( NumberFormatException e )
    {
      throw new ParseException( e.toString(), 0 );
    }
  }

  /**
   *  Validate newChar to see if it is an allowable character
   *
   */
  public boolean accepts( String oldVal, int oldPos, char newChar, String newVal, int newPos )
  {
    if( fieldSize > 0 && newPos > fieldSize )
      return false;

    // Next check if characters are allowable
    if( allowableChars != null )
    {
      for( int i = 0; i < allowableChars.length; i++ )
      {
	if( newChar == allowableChars[ i ] )
	  return true;
      }
      return false;
    }
    return true;
  }

  public Class getDataType()
  {
    return Double.class;
  }

  public static String formatInt(Integer number, int size) {
    return formatInt(number.intValue(), size);
  }
  public static String formatInt(int number, int size) {
    StringBuffer fmt = new StringBuffer(size+1);
    for(int i=0;i<size;i++) fmt.append("0");

    DecimalFormat df = new DecimalFormat(fmt.toString());

    return df.format(number);
  }

  public static String formatDouble(Double number, int size, int prec) {
    return formatDouble(number.doubleValue(), size, prec);
  }
  public static String formatDouble(double number, int size, int prec) {
    StringBuffer fmt = new StringBuffer(size+1);
    for(int i=0;i<size;i++) fmt.append("0");

    fmt.insert(size-prec,".");

    DecimalFormat df = new DecimalFormat(fmt.toString());

    String s = df.format(number);

    return s.substring(0,size-prec)+s.substring(size-prec+1);
  }



}
